程序员自我修炼:《匠艺整洁之道》读书总结
The following article is from 业务建模与微服务 Author 刘勇智
作为技术人员的我,心中始终有一些困惑和疑虑。我这几天翻看了鲍勃大叔的封山之作《匠艺整洁之道》,其中的部分困惑和疑虑终于得到了部分答案。下面是我的一些总结和思考,如果其中一些观点有失偏颇,欢迎大家一起来探讨。
下图是存在我心中的一些困惑和疑虑,鲍勃大叔从纪律、标准和职业操守这几个地方给了我答案。
上图中我们看到所有的人和问题都围绕着软件,同时我相信我们很多人都听过软件这个词和行业,那么它到底是什么意思呢?
1. 什么是软件
软件 software 中的第一个单词是 soft,所以软件应该是柔软的,也就是容易改动的。如果我们不期望它容易改动,那就是硬件 hardware。
我们发明软件的目的是为了使机器的行为易于改动。软件是用户与硬件之间的接口界面。用户主要是通过软件与计算机进行交流。它有两个价值:
行为价值。用户希望一个可以工作的软件来满足自己的需求。行为是软件的焦点,用户通过自己与软件的交互行为来改变机器的行为,从而满足自己的需求并解决自己的问题。
“柔软性”价值,也叫结构价值。就是适应业务变化,容易修改。没有任何软件产品能在与用户接触中幸存下来的,软件需要持续修改来解决用户新的问题。所以我们也说软件工程是一个持续修改本体的工程,软件一直在变化,之前的版本很难一直幸存下来。
软件工程师已经是现在这个时代最接近工匠的一个职业。我们不能仅仅把软件当做工程来做,软件更是一门手艺,甚至是一门艺术。不管是手艺,艺术还是匠艺,它们指的是懂得如何做好某件事情,源自于良好的实践和大量的经验。
2. 纪律
极限编程的生命之环也就是下图中心的四要素是工程实践手段:TDD(测试驱动开发)、重构、简单设计和结对编程(鲍勃大叔称为协同编程)。最左端的是验收测试,它是极限编程中最注重技术与工程的业务实践手段。
2.1 TDD
TDD 是关键纪律,没有 TDD,其他四个纪律无从谈起。在此鲍勃大叔用来近乎一半的篇幅在讲 TDD。TDD 的目标是创建程序员完全信赖的测试集。只要测试集通过,我们可以安心的部署代码。所以 TDD 主宰着软件开发的一切,贯穿从需求开始的 Tasking 到最后的生产部署,它能帮助程序员稳住节奏。这里有我关于 TDD 的一篇文章(TDD与瀑布式开发)可供参考。另外很多地方都有关于 TDD 的洞见,此处就不在赘述了。
但是这里要说一下测试替身,鲍勃大叔关于测试替身的讲述可能会帮助我们对测试替身课程的理解,很多地方把五种测试替身单独分开来讲的,忽略了其中的联系。五种测试替身 Dummy(仿品)、Stub(占位)、Spy(间谍)、Mock(拟造)和 Fake(伪造)的关系如下:
Stub 是 Dummy 的一种, Spy 是 Stub 的一种, Mock 是 Spy 的一种。Fake 则独立于外。
Dummy 是一种什么也不做的实现方式,测试中一般不会实际使用它。如果方法有返回值,Dummy 的返回值应尽量接近 null 或者 0。
Stub 是一种 Dummy,它也什么都不做。不过 Stub 一般不返回 null 或 0,而是返回测试所需的特定值,能推动函数沿着预定的测试路径前行。
Spy 是一种 Stub,它返回测试所需特定值,推动系统沿着我们期望的路径前行。但是 Spy 能记住它所做的事情,并允许测试调用询问。
Mock 是一种 Spy,它返回测试所需特定值,推动系统沿着我们期望的路径前行,能记住它所做的事情,不过 Mock 还知道我们的预期,基于这些预期能判断测试是否通过。但是 Mock 将 Spy 行为和测试断言绑死了。
2.2 重构
重构能帮助我们把结构糟糕的代码改为结构更好的代码,并且不会影响代码的行为,代码的行为不会改变那么对结构的改变就是安全的。那么如何保证改进不会影响行为?因为我们有 TDD,有我们完全信赖的测试集。与此同时,重构是和 TDD 结合最为紧密的,它是融入到 TDD 的步骤中的。
2.3 简单设计
没有重构,简单设计几乎无法实现。简单设计是重构的最终目标;重构是达成这一目标唯一可行的方法。简单设计包含四条极简的规则:
通过测试(鲍勃在本书中表达为用例覆盖)。可测试的代码就是解耦了的代码。随着时间的推移,测试的功用从沟通变为覆盖,重要性越来越高。没有良好、详尽的测试集,重构几乎无法做到。
揭示意图。设计要达到的基本目标就是让其他程序员能够容易地理解、改进和升级我们的系统。除了能让系统、代码表述软件能做什么,以及该怎么用,没有更好的路来实现这一目标。
减少重复。数据结构随着时间的推移发生变化,程序员不得不遍历代码中的全部重复,并且正确修改。遍历的重复越多,出现脆弱的危险性就越大。
最少元素。在满足上面3个条件后,我们应该努力减少函数内部的代码规模。关于函数的命名,记住,函数的名称长度与其涵盖范围应成反比。公有函数名称应该相对短,私有函数的名称应该更长一些。我们一般如何做到函数内部的代码规模尽量小呢?主要是抽取更多的函数,由于有精巧的小函数,一般都会有漂亮的长名字,因此更有利于使函数非常小,而且富有表现力。
2.4 协同编程
关于结对编程我有一篇文章(结对编程的根支叶果)专门讲的,这里不再赘述。结对编程不会提高我们的效率,但是它主要作用是在于有利于团队知识传递和技能提升,对 TDD、重构和简单设计起到了支撑作用,从而提高了团队的整体执行力。
2.5 验收测试
验收测试会将软件开发团队与业务绑定到一起。BA(业务分析师) 与 QA 负责编写验收测试如使用 Cucumber、SpecFlow、FitNesse等。BA 专注于正常的业务流程(Happy path),而 QA 专注于探索系统可能失败的路径。
验收测试集就是整个系统的需求文档,通过编写验收测试,BA 与 QA 要证明这些测试通过时,对应的业务功能也就完成了。验收测试的演进目标路线如下:
起步阶段程序员可以在 BA 和 QA 的指导下进行编写。中间目标是创建 BA 和 QA 能够阅读和认可的测试。最终目标是让 BA 和 QA 有能力来写测试。
2.6 小结
系统发布有纪律,高效率高质量的系统交付同样存在着有迹可循的纪律:TDD、重构、简单设计、协同编程和验收测试等。正是这些纪律决定了团队交付的效率和交付质量的底线。
3. 标准
标准是期望值的底线,我们不能越过这些底线,它们是最后的防线。可以高于标准,但永远不要低于标准。
3.1 生产力
永远不要shit,如果交付了shit,那么就要有缓解措施。
正如之前说的软件的第一个单词是 soft,其存在的全部理由是为了我们能快速地改变机器的行为,希望得到成本低廉的需求变更适应能力。
我们的软件在技术上时刻准备好发布,但是并不意味着企业想要的发布。时刻准备着意味着不希望开发团队要通知业务还要等待,它是一种态度,是一种不断提供增量价值的承诺。
3.2 质量
我们希望持续改进,随着时间的推移,系统的设计和架构得到改善,软件变得更加整洁。我们希望一切都会随着时间的推移而变得更好。那个如何消除这种系统随着时间而变得难以处理,团队免于恐惧呢?答案是遵守我们上面提到的纪律,我们将有信心和能力加速修正正在退化的系统,让软件保持在持续改进的轨道上。
不要把问题留给 QA,我们希望 QA 什么问题都不会发现。QA 不适合放到研发的末端,而是应该放到过程的开始,QA 的工作不是找到所有缺陷,这是程序员的工作。QA 的工作应该是测试来指定系统的行为,给出足够的细化测试,方便排除系统中的缺陷。这些测试应该由程序员而不是 QA 来执行。我们应该将大多数测试自动化,解放 QA 的双手,进行探索性测试。
3.3 勇气
团队中的成员是可以相互补位的。当有成员倒下时,团队中其他人能够接替他,直到倒下的成员重归岗位。那么如何才能做好补位呢?最好的办法就是协同编程。
我们要做好预估,程序员最靠谱的预估是:“我不知道”。因为这个预估混合了程序员所知和所不知的东西。当我们进行大项目的评估时,可以按照下面的预估来做:
有 5% 的可能在周五前完成
有 50% 的可能在下周五前完成
有 95% 的可能在下下周五完成
坦诚不确定最有价值。另外在敏捷实践中采用了故事点技术。故事点靠谱,因为其不承诺时限。故事点描述了一项任务相对于另一项任务的成本差异。故事点可以是任意数字,但是相互之间有关联。
程序员要懂得说“不”。因为我们是处于一线战场的人,我们的工作是找到一条通往“是”的道路。但有时“是”行不通,我们是唯一的知情者。当答案确实是“不”时,我们就得说“不”。
3.4 小结
“没有度量标准,就没法评估;没法评估,就没法改进”,正是这些度量的标准,在指导着交付效率和质量不断迭代进步。正是上面这些标准,使得程序员的匠艺不断精进。
4. 职业操守
现在的软件行业,几乎每 5 年,世界上的程序员数量就会翻一番。这意味着世界上有一般的程序员拥有不到 5 年的工作经验,只要按照这种速度继续下去,这个软件行业一直处于不稳定的状态----永远缺乏经验。
作为技术人员,我们可能会面对来自业务、上级等和自身不稳定的挑战,但是我们也要坚持自己的职业操守。所以鲍勃提出以下誓言,为捍卫和维护计算机程序员职业的荣誉,我承诺,尽我的能力和判断力:
我不写有害的代码。
我们的代码不能伤害自己的用户、自己的员工、自己的老板和同事。我们的代码不能是功能有害的,不能伤害社会。我们不应危害代码结构。应该保持代码整洁,保持代码结构良好。只要使得源代码难以阅读、难以理解、难以修改或重用,就是结构性伤害。那该如何做到呢?我们要从测试出发,先写测试,先清理测试,实践 TDD,遵守我们上面说到的五条纪律。
我生产的代码将永远是我最好的作品。我不会故意让那些在行为或结构上有缺陷的代码累积起来。
“先让它工作,再使其正确”,让程序工作只是第一步。很多人认为一旦程序工作了,就完事了。实际上这只是实现了软件的行为价值。然而,第二步清理代码,守护软件的结构价值。结构价值比行为价值更重要,这是因为软件系统必须能够响应需求的变化。结构越好,行为越好,就好测试、修改和重用,就能快速响应需求变化。结构价值大于行为价值;结构价值取决于良好的依赖管理;良好的依赖管理来自SOLID原则。因此系统的整体价值取决于SOLID原则的正确应用。
根据艾森豪威尔矩阵,我们根据事情的重要性和紧急性,把事情分为了四象限。如果事情不重要,就根本不该做,做不重要的事情纯属浪费。重要的事情是长期的,紧急的事情是短期的。软件的结构是长期的,行为是短期的,因此我们要关注软件的结构,行为次之。这个看似和“先让它工作,再使其正确”相互矛盾,实际上我们可以先实现行为,然后给予它正确的结构。这也就是 TDD “红 -> 绿 -> 重构”的节奏。
我将在每次发布时提供快速、确定和可重复的证据,证明代码的每个元素都能正常工作。
之前有人认为软件是一种数学,他希望我们构建由假设、定理、推论和引理的上层建筑。相反我们认识到软件不是一种数学,它是一门科学。我们需要通过实验来验证。我们在测试通过的基础上构建理论的上层建筑。那我们能找到里面正确性的证据吗?TDD 给了我们实验性的经验证明,我们每天都依赖于这种证明。我们构建的测试集能在短时间内运行,当测试集通过时,我们就可以知道可以放心交付了,而且这些测试集可以由任何人在任何时候重复运行。
我将经常进行小规模的发布,不妨碍其他人的进展。
我们要持续集成。提交越频繁,就越不可能面临合并。而且,如果真的合并,也会是极小的合并。同时持续集成要依赖于可靠的单元测试,减少合并的错误。
我们要持续部署。这样我们就能消除生产发布之间的延迟,最终的目标是能够持续、安全和无仪式的部署,部署应该可能地接近于无事发生,大家淡定自如。
我们要持续构建。如果要在短时间内部署,就要在短时间内构建。决不允许构建失败,如果你习惯了失败,你就会开始忽视它们。如果构建失败,团队要有人停止手头工作,处理这个紧急情况。
我将无畏地、毫不留情地利用一切机会改进我的创作。我绝不让它变更差。
我们要持续改进。我们有代码的童子军军规:我们的代码推送到远端时的版本要比之前的下载的版本更加整洁。怎么做到呢?我们可以增加覆盖率,使用突变测试,进行以改进为目标的重构。
我们不仅仅只关注代码。设计、时间表、和计划等都是应该不断改进的创造物,随着时间的推移让事情变得更好,我们不断改进我们所做的一切。
我将尽我所能尽可能地提高自己和他人的生产力。我不会做任何降低生产力的事。
“想要走得快,只能好好走”。保持代码、设计的整洁,保持高稳定的测试覆盖率等这些是提高生产力的间接方法。我们需要关注直接方法:生产力的主要组成部分不只是快速编写代码的能力,还有构建、测试、调试、部署等。所以我们如果发现构建缓慢,就需要找到原因并解决;我们如果发现测试缓慢,就不要让缓慢的东西拖累测试,把他们模拟出来,绕过它们,把缓慢测试从关键路径上移开;我们尽量不要调试,通过 TDD 来写单元测试帮助我们发现问题;部署是一套过程,将部署自动化,而且也要为这套过程写测试。
我们需要解决注意力分散问题。会议变得无聊时,就离开,但是请保持礼貌,询问与会者还需要自己吗?鲍勃大叔认为听音乐会妨碍自己专心工作,所以他抛弃了一边写代码一边听音乐的习惯。当心情不好时候,不要通过音乐或会议来掩饰这些情绪,需要采取行动,解决情绪问题,然后进入到编码工作状态。同时在时间管理上,鲍勃大叔推荐使用番茄工作法并提倡大家保卫番茄,确保在这个番茄时间钟内不被打扰。
我将一直确保其他人能够补上我的位置,我也能够为其他人补位。
将知识隔离成一个个筒仓对团队是非常有害的,一个人的损失会导致整块知识的丢失,团队会陷入瘫痪。解决此问题的办法就是在团队中传递知识,方式就是结对和结组。
团队成员非常频繁地彼此查看和互动也很重要,最好的方式就是放到一个屋子中。根据鲍勃的经验,将团队安排在同一间办公室,生产力就有了很大的提升。对于远程团队,应该尽可能在同一时间工作,跨时区尽量少,同时可以打造一个虚拟的办公室,比如分布式团队的大电视开开,大家可以相互看到,尽量创造出团队办公室的感觉。因为远程工作,大家都是隔着屏幕,很容易不把人当人看。就像是隔着汽车的挡风玻璃我们可能喝骂其他驾驶员一样。为了防止此类事情的发生,团队每年应该有几次能齐聚同一个真实的房间,有助于团队凝聚,始终成为其一个团队。很难想象你会想对待屏幕和其他驾驶员一样对待和你不久前刚共进过午餐的同事。
我将给出在数量级和精准度上都靠谱的预估。我不会做出没有把握的承诺。
在前面已经说过预估了。我们可以对三个数字进行预估:最好情况(5%的发生机会)、最坏情况(95%的发生机会)和普通情况(50%的发生机会)。也可以采用PERT的计划评估和审查技术。
如果我的程序员同事拥有足够的操守、标准、纪律和技能,就能赢得我的尊重。任何其他的属性或特征都不会成为我尊重程序员同事的因素。
我永远不会停止学习和改进我的技艺。
作为技术人员我们可以通过代码来编写规则,掌控着互联网运行,改变着世界。鲍勃大叔提出了我们必须遵守的职业操守,里面不仅有硬技能也有软技能。回答了我关于技术人要坚守的原则。
总结
通过阅读鲍勃大叔的这本匠艺整洁之道,如下图所示,我心中的的部分困惑和疑虑得到了解答。在后面的程序员修炼之路上,它给了技术人要遵守的纪律、标准和操守,让大家在通往匠师之路更加的顺畅。
这部大师著作上市后
高居图书榜第一
下单即减50,快快扫码抢购吧!
▼点击阅读原文,了解本书详情~